/*
 * This file is part of shared-drawing.
 * shared-drawing is free software: you can redistribute it and/or modify it 
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option )
 * any later version. shared-drawing is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * You should have received a copy of the GNU General Public License along with 
 * shared-drawing. If not, seehttp://www.gnu.org/licenses/.
 */
package SharedDrawing;
import common.StreamToInput;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdom2.Document;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;



/**
 * \brief SocketClient enables a client to send and listen messages from/to the server
 */

public class SocketClient extends Thread{
    private static final int timeoutMaxVal = 30;
    private Socket socketClnt;
    private InputStream stream;
    private PrintStream out;
    private String host; // \URGENT redondance avec Connexion manager
    private int port; // \URGENT redondance avec Connexion manager
    private static boolean connected = false;
    private StreamToInput transformer;
    private static SocketClient instance;

    /**
     * Constructor of the SocketClient class
     * @param host the remote address of the server
     * @param port the port on which we want to establish a connexion
     */
    private SocketClient(String host, int port) // TO TEST
    {
        this.host = host;
        this.port = port;
        connected = true;
        try {
            socketClnt = new Socket(this.host, this.port);
            stream = socketClnt.getInputStream();
            transformer = new StreamToInput();
        } catch (UnknownHostException ex) {
            //System.out.println("UnknownHostException");
            //Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
            connected = false;
        } catch (IOException ex) {
            System.out.println("IOException");
            connected = false;
            Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        if(connected)
        {
            try {
                out = new PrintStream(new BufferedOutputStream(this.socketClnt.getOutputStream()));
            } catch (IOException ex) {
                Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
                connected = false;
            }
        }
    }
    
    /**
     * to get the only instance of SocketClient since it is a singleton
     * @return the SocketClient instance
     */
    public static SocketClient getInstance()
    {
        if(connected == true)
            return instance;
        else
        {
            System.out.println("using instance of socket client while not connected");
            return null;
        }
                    
    }

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }
    
    /**
     * to connect on a server
     * @param host the remote address
     * @param port the listening port
     * @return true if the connexion was successful, else it returns false.
     */
    public static boolean connect(String host, int port)
    {
        if(connected == false)
        {
            instance = new SocketClient(host,port);
        }
        return connected;
    }

    /**
     * to disconnect from the server
     * @return returns true when the disconnection is effective, else it returns false
     */
    public boolean disconnect(){ // A TESTER
        try {
            this.socketClnt.close();
            connected = false;
        } catch (IOException ex) {
            System.out.println("can't disconnect");
            return false;
        }
        return true;
    }
    /**
     * to send a message to the server
     * @param doc is an xml document we send as a string
     */
    public void sendData(Document doc){
        XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
        String string = outputter.outputString(doc);
        System.out.println("sending : " +string);
        out.print(string);
        out.flush();
    }
    
    /**
     * since SocketClient extends thread, we need to run it. It will listen the socket forever (actually until we stop it)
     */
    @Override
    public void run()
    {
        String s;
        //create a parser
        XMLReader saxReader = null;
        try {
            saxReader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
            saxReader.setContentHandler(new XmlReader());
        } catch (SAXException ex) {
            Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(1);
        } catch (IOException ex) {
            Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(1);
        }
        //listen the server
        while(connected)
        {
            try {
                // now we got a String (model), now the controller can process it
                InputSource is = transformer.transform(stream);
                if(is == null)
                {
                    System.out.println("connexion broken");
                    disconnect();
                }
                else
                {
                    saxReader.parse(is);
                }
            } catch (SAXException ex) {
                Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
                System.exit(1);
            } catch (IOException ex) {
                Logger.getLogger(SocketClient.class.getName()).log(Level.SEVERE, null, ex);
                System.exit(1);
            }
        }
    }

    /*
     * Getters (no useless setters)
     */

    /**
     * to get the host address
     * @return a string containing the host address
     * \URGENT redondance avec Connexion manager
     */
    public String getHost() {
        return host;
    }
    /**
     * \brief to get the communication port
     * @return an integer containing the listening port
     * \URGENT redondance avec Connexion manager
     */
    public int getPort() {
        return port;
    }
    /**
     * to get the socket used in this instance
     * @return socketClnt
     */
    public Socket getSocketClnt() {
        return socketClnt;
    }

    public static boolean isConnected() {
        return connected;
    }
    
    
}
